#include "CBL_FreeList.h"

CB_SUBNAMESPACE_START(Memory)


CFreeList::CFreeList(uint32 _BlockSizeByte, uint32 _BlocksPerList, uint32 _Prealloc, bool _AssertLimit)
    : m_BlockSizeByte(_BlockSizeByte)
    , m_BlocksPerList(_BlocksPerList)
    , m_AssertLimit(_AssertLimit)
    , m_Capacity(_Prealloc)
    , m_ReallocationSize(_Prealloc)
{
    m_DataBlocks.reserve(m_Capacity);
    m_FreeBlocks.reserve(m_Capacity * m_BlocksPerList);
    AllocateBlocks();
}

// -------------------------------------------------------------------------------------------------

CFreeList::~CFreeList()
{
    for (uint32 i(0u); i<m_FreeBlockIndex; ++i)
    {
        delete[] m_DataBlocks[i];
    }
}

// -------------------------------------------------------------------------------------------------

void CFreeList::AllocateBlocks()
{
	if (m_DataBlocks.size() == m_Capacity)
	{
		Debug::Assert(m_AssertLimit, "Performance warning.\nA freelist has to allocate more memory than expected.");
		m_Capacity += m_ReallocationSize;
		
		m_DataBlocks.reserve(m_Capacity);
		m_FreeBlocks.reserve(m_Capacity * m_BlocksPerList);
	}
	
	m_DataBlocks.push_back(new uint8[m_BlockSizeByte * m_BlocksPerList]);
	m_pNextFreeBlock = m_DataBlocks[m_DataBlocks.size()-1];
	m_FreeBlockIndex = 0;
}

// -------------------------------------------------------------------------------------------------

void* CFreeList::NextAvailableBlock()
{
	void* pBlock;
	
	if (m_FreeBlocks.empty())
	{
		if (m_FreeBlockIndex >= m_BlocksPerList)
		{
			AllocateBlocks();
		}
		
		pBlock = m_pNextFreeBlock;
		m_pNextFreeBlock += m_BlockSizeByte;
		++m_FreeBlockIndex;
	}
	else
	{
		pBlock = m_FreeBlocks.back();
		m_FreeBlocks.pop_back();
	}
	
	return reinterpret_cast<void*>(pBlock);
}

// -------------------------------------------------------------------------------------------------

CB_SUBNAMESPACE_END